import { MoveDirection, Points, Shape } from "./types";
import GameConfig from './GameConfig'
import { SquareGrounp } from "./SquareGrounp";
import { Square } from "./Square";

/**
 * 判断一个函数是否为坐标
 * @param obj
 * @returns 
 */
function isPoint(obj: any): obj is Points {
  if (typeof obj.x === 'undefined') {
    return false
  }
  return true
}

/**
 * 该类中提供一系列的函数,更据游戏规则判断各种情况
 */
export class TerisRule {
  /**
   * 判断某个形状的方块是否能够移动到目标位置
   * @param shape 形状
   * @param target 中心点目标位置
   * @param exists 已存在的方块
   * @returns 是否可以移动到目标位置
   */
  static canIMove(shape: Shape, target: Points, exists: Square[]): boolean {
    // 约束条件  边界

    // 假设,中心点已经移动到了目标位置, 算出每个小方块的坐标
    const targetSquarePoints: Points[] = shape.map(sq => {
      return {
        x: sq.x + target.x,
        y: sq.y + target.y,
      }
    })

    // 边界判断 
    const isBooder = targetSquarePoints.some(p => {
      // 是否超出边界
      const { width, height } = GameConfig.panelSize
      return p.x > width - 1 || p.x < 0 || p.y < 0 || p.y > height - 1
    })
    if (isBooder) return false

    // 判断是否与已有方块有重叠
    const isOverlap = targetSquarePoints.some(p => {
      return exists.some(it => it.point.x == p.x && it.point.y == p.y)
    })

    return !isOverlap
  }

  /**
   * 移动方法
   * @param teris 方块
   * @param targetOrDirection 目标位置/移动方向
   * @param exists 已存在的方块
   * @returns 是否移动成功
   */
  static move(teris: SquareGrounp, target: Points, exists: Square[]): boolean;
  static move(teris: SquareGrounp, direction: MoveDirection, exists: Square[]): boolean;
  static move(teris: SquareGrounp, targetOrDirection: Points | MoveDirection, exists: Square[]): boolean {
    let target: Points;
    if (isPoint(targetOrDirection)) { // 坐标
      target = targetOrDirection
    } else { // 移动方向
      const direction = targetOrDirection
      let { x, y } = teris.centerPoint
      if (direction === MoveDirection.down) {
        y += 1
      } else if (direction === MoveDirection.left) {
        x -= 1
      } else if (direction === MoveDirection.right) {
        x += 1
      }
      target = { x, y }
    }
    const isMove = this.canIMove(teris.shape, target, exists)
    if (isMove) {
      teris.centerPoint = target
    }
    return isMove
  }

  /**
   * 将当前方块移动当前方向到不能移动为止
   * @param teris 
   * @param exists
   * @param direction 
   */
  static moveDirectly(teris: SquareGrounp, direction: MoveDirection, exists: Square[]) {
    while (this.move(teris, direction, exists)) { }
  }

  /**
   * 是否可以旋转成功
   * @param teris 
   * @param exists
   * @returns 
   */
  static rotate(teris: SquareGrounp, exists: Square[]): boolean {
    const newShape = teris.afterRotateShape()
    const isRotate = this.canIMove(newShape, teris.centerPoint, exists)
    if (isRotate) {
      teris.rotate()
    }
    return isRotate
  }

  /**
   * 从存在的方块中消除,并返回消除的行数
   * @param exists 
   * @returns 消除了几行
   */
  static deleteSquares(exists: Square[]): number {
    const ys = exists.map(sq => sq.point.y)
    let num = 0
    let minY = Math.min(...ys)
    let maxY = Math.max(...ys)
    for (let y = minY; y <= maxY; y++) {
      if (this.deleteLine(exists, y)) {
        num++
      }
    }
    return num
  }

  /**
   * 消除一行
   * @param exists 
   * @param y 
   * @returns 
   */
  private static deleteLine(exists: Square[], y: number): boolean {
    const squares = exists.filter(sq => sq.point.y === y)
    if (squares.length == GameConfig.panelSize.width) {
      squares.forEach(sq => {
        // 界面移除
        sq.viewer?.remove()
        // 数组中移除
        exists.splice(exists.indexOf(sq), 1)
      });
      // 在移除方块上面的方块向下移动
      exists.filter(sq => sq.point.y < y).forEach(sq => sq.point = { x: sq.point.x, y: sq.point.y + 1 })
      return true
    }
    return false
  }
}